﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using Terraria;
using Terraria.GameContent;
using Terraria.ID;

namespace EAS.Common.ModUtils
{
    public static class AccUtil
    {
        public static void MinionAntiClump(this Projectile projectile, float pushForce = 0.05f)
        {
            for (int k = 0; k < Main.maxProjectiles; k++)
            {
                Projectile otherProj = Main.projectile[k];
               
                if (!otherProj.active || otherProj.owner != projectile.owner || !otherProj.minion || k == projectile.whoAmI)
                    continue;
                bool sameProjType = otherProj.type == projectile.type;
                float taxicabDist = Math.Abs(projectile.position.X - otherProj.position.X) + Math.Abs(projectile.position.Y - otherProj.position.Y);
                if (sameProjType && taxicabDist < projectile.width)
                {
                    if (projectile.position.X < otherProj.position.X)
                        projectile.velocity.X -= pushForce;
                    else
                        projectile.velocity.X += pushForce;

                    if (projectile.position.Y < otherProj.position.Y)
                        projectile.velocity.Y -= pushForce;
                    else
                        projectile.velocity.Y += pushForce;
                }
            }
        }
        public static Vector2 RotateVector(Vector2 origin, Vector2 vecToRot, float rot)
        {
            return new Vector2((float)(Math.Cos((double)rot) * (vecToRot.X - (double)origin.X) - Math.Sin((double)rot) * (vecToRot.Y - (double)origin.Y)) + origin.X, (float)(Math.Sin((double)rot) * (vecToRot.X - (double)origin.X) + Math.Cos((double)rot) * (vecToRot.Y - (double)origin.Y)) + origin.Y);
        }
        public static Vector2 SafeDirectionTo(this Entity entity, Vector2 destination, Vector2? fallback = null)
        {
            if (fallback == null)
            {
                fallback = new Vector2?(Vector2.Zero);
            }
            return (destination - entity.Center).SafeNormalize(fallback.Value);
        }
        public static NPC MinionHoming(this Vector2 origin, float maxDistanceToCheck, Player owner, bool ignoreTiles = true, bool checksRange = false)
        {
            if (owner == null || !owner.whoAmI.WithinBounds(255) || !owner.MinionAttackTargetNPC.WithinBounds(200))
            {
                return origin.ClosestNPCAt(maxDistanceToCheck, ignoreTiles, false);
            }
            NPC npc = Main.npc[owner.MinionAttackTargetNPC];
            bool canHit = true;
            if (!ignoreTiles)
            {
                canHit = Collision.CanHit(origin, 1, 1, npc.Center, 1, 1);
            }
            float extraDistance = npc.width / 2 + npc.height / 2;
            bool distCheck = Vector2.Distance(origin, npc.Center) < maxDistanceToCheck + extraDistance || !checksRange;
            if (owner.HasMinionAttackTargetNPC && canHit && distCheck)
            {
                return npc;
            }
            return origin.ClosestNPCAt(maxDistanceToCheck, ignoreTiles, false);
        }
        public static bool WithinBounds(this int index, int cap)
        {
            return index >= 0 && index < cap;
        }
        public static NPC ClosestNPCAt(this Vector2 origin, float maxDistanceToCheck, bool ignoreTiles = true, bool bossPriority = false)
        {
            NPC closestTarget = null;
            float distance = maxDistanceToCheck;
            if (bossPriority)
            {
                bool bossFound = false;
                for (int index = 0; index < Main.npc.Length; index++)
                {
                    if ((!bossFound || Main.npc[index].boss || Main.npc[index].type == NPCID.WallofFleshEye) && Main.npc[index].CanBeChasedBy(null, false))
                    {
                        float extraDistance = Main.npc[index].width / 2 + Main.npc[index].height / 2;
                        bool canHit = true;
                        if (extraDistance < distance && !ignoreTiles)
                        {
                            canHit = Collision.CanHit(origin, 1, 1, Main.npc[index].Center, 1, 1);
                        }
                        if (Vector2.Distance(origin, Main.npc[index].Center) < distance + extraDistance && canHit)
                        {
                            if (Main.npc[index].boss || Main.npc[index].type == NPCID.WallofFleshEye)
                            {
                                bossFound = true;
                            }
                            distance = Vector2.Distance(origin, Main.npc[index].Center);
                            closestTarget = Main.npc[index];
                        }
                    }
                }
            }
            else
            {
                for (int index2 = 0; index2 < Main.npc.Length; index2++)
                {
                    if (Main.npc[index2].CanBeChasedBy(null, false))
                    {
                        float extraDistance2 = Main.npc[index2].width / 2 + Main.npc[index2].height / 2;
                        bool canHit2 = true;
                        if (extraDistance2 < distance && !ignoreTiles)
                        {
                            canHit2 = Collision.CanHit(origin, 1, 1, Main.npc[index2].Center, 1, 1);
                        }
                        if (Vector2.Distance(origin, Main.npc[index2].Center) < distance + extraDistance2 && canHit2)
                        {
                            distance = Vector2.Distance(origin, Main.npc[index2].Center);
                            closestTarget = Main.npc[index2];
                        }
                    }
                }
            }
            return closestTarget;
        }
        public static void DrawAfterimagesCentered(Projectile proj, int mode, Color lightColor, int typeOneIncrement = 1, Texture2D texture = null, bool drawCentered = true)
        {
            if (texture == null)
            {
                texture = TextureAssets.Projectile[proj.type].Value;
            }
            int frameHeight = texture.Height / Main.projFrames[proj.type];
            int frameY = frameHeight * proj.frame;
            float scale = proj.scale;
            float rotation = proj.rotation;
            Rectangle rectangle = new Rectangle(0, frameY, texture.Width, frameHeight);
            Vector2 origin = rectangle.Size() / 2f;
            SpriteEffects spriteEffects = SpriteEffects.None;
            if (proj.spriteDirection == -1)
            {
                spriteEffects = SpriteEffects.FlipHorizontally;
            }
            bool failedToDrawAfterimages = false;
           
                Vector2 centerOffset = drawCentered ? (proj.Size / 2f) : Vector2.Zero;
                switch (mode)
                {
                    case 0:
                        for (int i = 0; i < proj.oldPos.Length; i++)
                        {
                            Vector2 drawPos = proj.oldPos[i] + centerOffset - Main.screenPosition + new Vector2(0f, proj.gfxOffY);
                            Color color = proj.GetAlpha(lightColor) * ((float)(proj.oldPos.Length - i) / (float)proj.oldPos.Length);
                            Main.spriteBatch.Draw(texture, drawPos, new Rectangle?(rectangle), color, rotation, origin, scale, spriteEffects, 0f);
                        }
                        break;
                    case 1:
                        {
                            int increment = Math.Max(1, typeOneIncrement);
                            Color drawColor = proj.GetAlpha(lightColor);
                            int afterimageCount = ProjectileID.Sets.TrailCacheLength[proj.type];
                            for (int j = 0; j < afterimageCount; j += increment)
                            {
                                Vector2 drawPos2 = proj.oldPos[j] + centerOffset - Main.screenPosition + new Vector2(0f, proj.gfxOffY);
                                if (j > 0)
                                {
                                    float colorMult = (float)(afterimageCount - j);
                                    drawColor *= colorMult / ((float)afterimageCount * 1.5f);
                                }
                                Main.spriteBatch.Draw(texture, drawPos2, new Rectangle?(rectangle), drawColor, rotation, origin, scale, spriteEffects, 0f);
                            }
                            break;
                        }
                    case 2:
                        for (int k = 0; k < proj.oldPos.Length; k++)
                        {
                            float afterimageRot = proj.oldRot[k];
                            SpriteEffects sfxForThisAfterimage = (proj.oldSpriteDirection[k] == -1) ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
                            Vector2 drawPos3 = proj.oldPos[k] + centerOffset - Main.screenPosition + new Vector2(0f, proj.gfxOffY);
                            Color color2 = proj.GetAlpha(lightColor) * ((float)(proj.oldPos.Length - k) / (float)proj.oldPos.Length);
                            Main.spriteBatch.Draw(texture, drawPos3, new Rectangle?(rectangle), color2, afterimageRot, origin, scale, sfxForThisAfterimage, 0f);
                        }
                        break;
                    default:
                        failedToDrawAfterimages = true;
                        break;
                }
            
            if (ProjectileID.Sets.TrailCacheLength[proj.type] <= 0 || failedToDrawAfterimages)
            {
                Vector2 startPos = drawCentered ? proj.Center : proj.position;
                Main.spriteBatch.Draw(texture, startPos - Main.screenPosition + new Vector2(0f, proj.gfxOffY), new Rectangle?(rectangle), proj.GetAlpha(lightColor), rotation, origin, scale, spriteEffects, 0f);
            }
        }
    }

}

